package de.bomzhi.mz.pflection.views;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;

import com.powerflasher.fdt.core.dom.IDeclarationBindable;
import com.powerflasher.fdt.core.outermodel.AbstractSourceContainerModel;
import com.powerflasher.fdt.core.outermodel.AbstractSourceUnitContainerModel;
import com.powerflasher.fdt.core.outermodel.FDTModel2;
import com.powerflasher.fdt.core.outermodel.IMasterModel;
import com.powerflasher.fdt.core.outermodel.IOuterModelElement;
import com.powerflasher.fdt.core.outermodel.ProjectModel;
import com.powerflasher.fdt.core.outermodel.binding.Binding;
import com.powerflasher.fdt.core.resources.IFlashProject;
import com.powerflasher.fdt.ui.UIActions;
import com.powerflasher.fdt.ui.views.flashExplorer.FlashExplorer;

import de.bomzhi.mz.pflection.model.IndexOfModelSets;
import de.bomzhi.mz.pflection.model.SetOfModels;
import de.bomzhi.mz.pflection.model.SetOfStrongTypedModels;

public class FDTPflectionView extends ViewPart implements IRefreshableView{

	/**
	 * The ID of the view as specified by the extension.
	 */
	public static final String ID = "de.bomzhi.mz.pflection.views.FDTPflectionView";

	private TableViewer viewer1;
	private TableViewer viewer2;
	private TableViewer viewer3;
	private ISelectionListener globalSelectionListener = new ISelectionListener() {
		public void selectionChanged(IWorkbenchPart sourcepart, ISelection selection) {
			if (selection instanceof IStructuredSelection) {
				IStructuredSelection ss = (IStructuredSelection) selection;
				computeRelevantSelectedItems(ss.toArray());
			}
		}
	};

	private PflectionContentProvider metricsContentProvider;

	private Set<IResource> lastSelectedResources = new HashSet<IResource>();

	private final class SelectionChanged implements
			ISelectionChangedListener {
		private final TableViewer viewer;

		public SelectionChanged(TableViewer viewer2) {
			viewer = viewer2;
		}

		public void selectionChanged(SelectionChangedEvent arg0) {
			IStructuredSelection selection = (IStructuredSelection)arg0.getSelection();
			Object firstElement = selection.getFirstElement();
			// TODO remove this hack!!!
			if(arg0.getSource() == viewer1 && viewer3.getContentProvider()!=null){
				viewer3.setInput(new Object[0]);
				
			}
			if(firstElement instanceof SetOfStrongTypedModels<?>){
				viewer.setContentProvider(new IStructuredContentProvider(){

					public Object[] getElements(Object arg0) {
						if(arg0 instanceof SetOfStrongTypedModels<?>)
							return ((SetOfStrongTypedModels<?>) arg0).getModels().toArray();
						return new Object[0];
					}

					public void dispose() {}
					public void inputChanged(Viewer arg0, Object arg1, Object arg2) {}
				});
				viewer.setLabelProvider(new PflectionLabelProvider());
				viewer.addDoubleClickListener(new OpenDeclarationOnDblClick());
				viewer.setInput(firstElement);
			} else if(firstElement instanceof IndexOfModelSets<?>){
				viewer.setContentProvider(new IStructuredContentProvider() {

					public Object[] getElements(Object inputElement) {
						if(inputElement instanceof IndexOfModelSets<?>){
							return ((IndexOfModelSets<?>)inputElement).getModels();
						}
						return new Object[0];
					}

					public void dispose() {	}

					public void inputChanged(Viewer viewer,
							Object oldInput, Object newInput) {	}
					
				});
				viewer.setInput(firstElement);
				viewer.setLabelProvider(new ViewLabelProvider());
			} 
			
		}
	}

	private final class OpenDeclarationOnDblClick implements
			IDoubleClickListener {
		public void doubleClick(DoubleClickEvent arg0) {
			IStructuredSelection selection = (IStructuredSelection)arg0.getSelection();
			Object element = selection.getFirstElement();
			if(element instanceof IOuterModelElement && !(element instanceof AbstractSourceUnitContainerModel)){
				((IOuterModelElement) element).performActionOnSource(UIActions.OPEN_EDITOR);
			}else if(element instanceof AbstractSourceUnitContainerModel){
				FlashExplorer activeFlashExplorer = FlashExplorer.getActiveFlashExplorer();
				if(activeFlashExplorer!=null){
					activeFlashExplorer.selectReveal(new StructuredSelection(((AbstractSourceUnitContainerModel)element).getSource()));
				}
			}
		}
	}

	static class ViewLabelProvider extends LabelProvider implements ITableLabelProvider {
		public String getColumnText(final Object obj, final int index) {
			if(obj instanceof SetOfStrongTypedModels<?>){
				return ((SetOfStrongTypedModels<?>)obj).getDescription();
			}else if(obj instanceof IndexOfModelSets<?>){
				return ((IndexOfModelSets<?>) obj).getDescription();
			}
			return getText(obj);
		}

		public Image getColumnImage(final Object obj, final int index) {
			final PflectionLabelProvider labelProvider = new PflectionLabelProvider();
			if(obj instanceof SetOfStrongTypedModels<?>){
				return labelProvider.getImage(((SetOfStrongTypedModels<?>) obj).getFirst());
			}
			return getImage(obj);
		}

		public Image getImage(final Object obj) {
			return PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_ELEMENT);
		}
	}

	public FDTPflectionView() {
	}
	
	public void refresh() {
		computeRelevantSelectedItems(lastSelectedResources.toArray());
	}
	
	protected void computeRelevantSelectedItems(Object[] selectedItems) {
		SetOfModels selectedModels = new SetOfModels();
		lastSelectedResources.clear();
		for (Object selectedItem : selectedItems) {
			if (selectedItem instanceof IResource) {
				IResource selectedResource = (IResource) selectedItem;
				lastSelectedResources.add(selectedResource);
				IProject project = selectedResource.getProject();
				IFlashProject flashProject = (IFlashProject) project.getAdapter(IFlashProject.class);
				if (!flashProject.isBuild()) {
					continue;
				}
				ProjectModel projectModel = FDTModel2.getInstance().getProjectModel(project);
				if (selectedResource instanceof IFile) {
					IFile selectedFile = (IFile) selectedResource;
					IMasterModel masterModel = FDTModel2.getInstance().getModelForRecource(selectedFile);
					if (masterModel != null) {
						selectedModels.add(masterModel);
						continue;
					}
					AbstractSourceContainerModel sourceContainer = projectModel.getSourceContainer(selectedFile);
					if (sourceContainer != null) {
						selectedModels.add(sourceContainer);
						continue;
					}
				} else if (selectedResource instanceof IFolder) {
					AbstractSourceUnitContainerModel sourceContainer = projectModel
							.getSourceContainer(selectedResource);
					if (sourceContainer != null) {
						selectedModels.add(sourceContainer);
						continue;
					}
					sourceContainer = projectModel.getPackageForResource(selectedResource);
					if (sourceContainer != null) {
						selectedModels.add(sourceContainer);
						continue;
					}
				} else if (selectedResource instanceof IProject) {
					selectedModels.add(projectModel);
				}
			} else if (selectedItem instanceof IOuterModelElement) {
				selectedModels.add((IOuterModelElement) selectedItem);
			} else if (selectedItem instanceof Binding) {
				IOuterModelElement target = ((Binding) selectedItem).getTarget();
				if (target != null) {
					selectedModels.add(target);
				}
			} else if (selectedItem instanceof IDeclarationBindable) {
				IOuterModelElement target = ((IDeclarationBindable) selectedItem).getDeclarationTarget();
				if (target != null) {
					selectedModels.add(target);
				}
			}
		}
		viewer1.setInput(selectedModels);
		viewer2.setInput(new Object[0]);
		viewer3.setInput(new Object[0]);
	}

	/**
	 * This is a callback that will allow us to create the viewer and initialize
	 * it.
	 */
	public void createPartControl(Composite parent) {

		viewer1 = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		metricsContentProvider = new PflectionContentProvider();
		viewer1.setContentProvider(metricsContentProvider);
		viewer1.setLabelProvider(new ViewLabelProvider());
		viewer1.setInput(getViewSite());
		
		viewer2 = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		viewer2.setContentProvider(new NullContentProvider());
		viewer2.setComparator(new ViewerComparator());
		viewer3 = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
		viewer3.setContentProvider(new NullContentProvider());
		viewer3.setComparator(new ViewerComparator());
		
		viewer1.addSelectionChangedListener(new SelectionChanged(viewer2));
		viewer2.addSelectionChangedListener(new SelectionChanged(viewer3));

		// Create the help context id for the viewer's control
		PlatformUI.getWorkbench().getHelpSystem().setHelp(viewer1.getControl(), "de.bomzhi.mz.metrics.viewer");
		getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(globalSelectionListener);
		
	}

	/**
	 * Passing the focus request to the viewer's control.
	 */
	public void setFocus() {
		viewer1.getControl().setFocus();
	}

	@Override
	public void dispose() {
		getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(globalSelectionListener);
		super.dispose();
	}

}